package cn.wolfcode.business.service.impl;

import cn.wolfcode.business.domain.BusBpmnInfo;
import cn.wolfcode.business.mapper.BusBpmnInfoMapper;
import cn.wolfcode.business.service.IBusBpmnInfoService;
import cn.wolfcode.business.utils.DateUtil;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * 流程定义明细Service业务层处理
 * 
 * @author wolfcode
 * @date 2025-08-26
 */
@Service
public class BusBpmnInfoServiceImpl implements IBusBpmnInfoService 
{
    @Autowired
    private BusBpmnInfoMapper busBpmnInfoMapper;
    @Autowired
    private RepositoryService repositoryService;

    /**
     * 查询流程定义明细
     * 
     * @param id 流程定义明细主键
     * @return 流程定义明细
     */
    @Override
    public BusBpmnInfo selectBusBpmnInfoById(Long id)
    {
        return busBpmnInfoMapper.selectBusBpmnInfoById(id);
    }

    /**
     * 查询流程定义明细列表
     * 
     * @param busBpmnInfo 流程定义明细
     * @return 流程定义明细
     */
    @Override
    public List<BusBpmnInfo> selectBusBpmnInfoList(BusBpmnInfo busBpmnInfo)
    {
        if(busBpmnInfo == null){
            throw new RuntimeException("非法参数");
        }
        String endDeployTime = (String) busBpmnInfo.getParams().get("endDeployTime");
        Date date = DateUtil.parseDate(endDeployTime, "yyyy-MM-dd");
        date = DateUtil.getModifyTime(date, Calendar.DAY_OF_YEAR, 1);
        date = DateUtil.getModifyTime(date, Calendar.SECOND, -1);
        String endTime = DateUtil.Date2String(date, "yyyy-MM-dd HH:mm:ss");
        busBpmnInfo.getParams().put("endDeployTime", endTime);
        return busBpmnInfoMapper.selectBusBpmnInfoList(busBpmnInfo);
    }

    /**
     * 新增流程定义明细
     * 
     * @param busBpmnInfo 流程定义明细
     * @return 结果
     */
    @Override
    public int insertBusBpmnInfo(BusBpmnInfo busBpmnInfo)
    {
        return busBpmnInfoMapper.insertBusBpmnInfo(busBpmnInfo);
    }

    /**
     * 修改流程定义明细
     * 
     * @param busBpmnInfo 流程定义明细
     * @return 结果
     */
    @Override
    public int updateBusBpmnInfo(BusBpmnInfo busBpmnInfo)
    {
        return busBpmnInfoMapper.updateBusBpmnInfo(busBpmnInfo);
    }

    /**
     * 撤销流程定义明细
     * 
     * @param ids 需要删除的流程定义明细主键
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteBusBpmnInfoByIds(Long[] ids)
    {
        // 参数合理化验证
        if(ids == null || ids.length == 0){
            throw new RuntimeException("非法参数");
        }
        Long id = ids[0];
        // 根据 id 查询出 bpmnInfo 对象
        BusBpmnInfo bpmnInfo = this.selectBusBpmnInfoById(id);
        if(bpmnInfo == null){
            throw new RuntimeException("非法参数");
        }
        // 查询到指定流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(bpmnInfo.getProcessDefinitionKey())
                .processDefinitionVersion(bpmnInfo.getVersion())
                .singleResult();
        // 根据流程定义对象拿到部署 ID
        String deploymentId = processDefinition.getDeploymentId();
        // 删除流程定义
        repositoryService.deleteDeployment(deploymentId, true);
        // 删除业务表数据
        return busBpmnInfoMapper.deleteBusBpmnInfoByIds(ids);
    }

    /**
     * 删除流程定义明细信息
     * 
     * @param id 流程定义明细主键
     * @return 结果
     */
    @Override
    public int deleteBusBpmnInfoById(Long id)
    {
        return busBpmnInfoMapper.deleteBusBpmnInfoById(id);
    }

    /**
     * 部署方法
     * @param file 文件
     * @param bpmnType 类型
     * @param info 信息
     * @return 成功||失败
     */
    @Override
    @Transactional
    public int deploy(MultipartFile file, Integer bpmnType, String info) throws IOException {
        if(file == null || bpmnType == null || info == null){
            throw new RuntimeException("非法参数");
        }
        // 获取到文件名字
        String fileName = file.getOriginalFilename();
        // 文件类型校验
        String subType = FilenameUtils.getExtension(fileName);
        if(!("xml".equalsIgnoreCase(subType) || "bpmn".equalsIgnoreCase(subType))){
            throw new RuntimeException("文件格式上传必须是 xml 或 bpmn");
        }
        // 使用 Activiti7 进行部署操作
        Deployment deploy = repositoryService.createDeployment()
                .addInputStream(fileName, file.getInputStream())
                .deploy();
        // 根据部署 id 查询流程定义对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        // 封装 Bpmn 对象
        BusBpmnInfo bpmnInfo = new BusBpmnInfo();
        bpmnInfo.setBpmnLabel(fileName);
        bpmnInfo.setBpmnType(bpmnType);
        bpmnInfo.setInfo(info);
        bpmnInfo.setDeployTime(deploy.getDeploymentTime());
        bpmnInfo.setProcessDefinitionKey(processDefinition.getKey());
        bpmnInfo.setVersion(processDefinition.getVersion());
        // 最终将我们业务数据插入到数据库中
        return busBpmnInfoMapper.insertBusBpmnInfo(bpmnInfo);
    }

    /**
     * 查看流程资源
     * @param id bpmnId
     * @param type 查询类型
     * @return 数据流
     */
    @Override
    @Transactional
    public InputStream readResources(Long id, String type) {
        // 参数合理化验证
        if(id == null || type == null){
            throw new RuntimeException("非法参数");
        }
        // 根据 id 查询 bpmnInfo 对象。
        BusBpmnInfo bpmnInfo = this.selectBusBpmnInfoById(id);
        if(bpmnInfo == null){
            throw new RuntimeException("非法参数");
        }
        // 获取到对象中的 processDefinitionKey 和 version
        String processDefinitionKey = bpmnInfo.getProcessDefinitionKey();
        Integer version = bpmnInfo.getVersion();
        // 根据上面获取到的 key 和 version 来查询流程定义对象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .processDefinitionVersion(version)
                .singleResult();
        if("xml".equalsIgnoreCase(type)){
            // 查看 XML 文件
            // 通过流程引擎对象获取到我们 xml 资源文件的名字
            String xmlFileName = processDefinition.getResourceName();
            // 通过 Activiti7 Api 来获取输入流
            InputStream xmlStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), xmlFileName);
            return xmlStream;
        } else {
            // 查看 PNG 图片
            // 我们需要创建可以绘制流程图的类
            DefaultProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
            // 获取 Model 模型对象。根据我们的流程定义才可以获取
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
            // 绘制流程图
            /**
             * 第一个参数：数据模型对象（有哪些节点，哪些连线）。
             * 第二个参数： List<>  哪些节点需要高亮。
             * 第三个参数： List<>  哪些连线需要高亮。
             * 第四个-第六个：都是使用的哪种字体标识
             */
            InputStream pngStream = processDiagramGenerator.generateDiagram(
                    bpmnModel,
                    Collections.emptyList(),
                    Collections.emptyList(),
                    "宋体",
                    "宋体",
                    "宋体"
            );
            return pngStream;
        }
    }

    /**
     * 根据 bpmn_type 查询 bpmnInfo 对象
     * @param flowAuditType bpmn_type
     * @return bpmnInfo 对象
     */
    @Override
    public BusBpmnInfo selectBpmnInfoByType(Integer flowAuditType) {
        return busBpmnInfoMapper.selectBpmnInfoByType(flowAuditType);
    }
}
